#include "binview.h"
#include "ui_binview.h"

BinView::BinView(QByteArray data, QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::BinView)
{
    ui->setupUi(this);

    ui->toolBoxSearch->removeItem(0);
    ui->toolBoxSearch->hide();

    int lenh = 19;

    byte1ASCII = QPoint(2, 1 * lenh + lenh / 2);
    byte1BIN = QPoint(2, 2 * lenh + lenh / 2);
    byte1HEX = QPoint(2, 3 * lenh + lenh / 2);
    byte1INT = QPoint(2, 4 * lenh + lenh / 2);
    byte1UINT = QPoint(2, 5 * lenh + lenh / 2);
    byte1OCT = QPoint(2, 6 * lenh + lenh / 2);

    byte2BIN = QPoint(2, 8 * lenh + lenh / 2);
    byte2HEX = QPoint(2, 9 * lenh + lenh / 2);
    byte2INT = QPoint(2, 10 * lenh + lenh / 2);
    byte2UINT = QPoint(2, 11 * lenh + lenh / 2);
    byte2OCT = QPoint(2, 12 * lenh + lenh / 2);

    byte4BIN = QPoint(2, 14 * lenh + lenh / 2);
    byte4HEX = QPoint(2, 15 * lenh + lenh / 2);
    byte4INT = QPoint(2, 16 * lenh + lenh / 2);
    byte4UINT = QPoint(2, 17 * lenh + lenh / 2);
    byte4OCT = QPoint(2, 18 * lenh + lenh / 2);

    byte8BIN = QPoint(2, 20 * lenh + lenh / 2);
    byte8HEX = QPoint(2, 21 * lenh + lenh / 2);
    byte8INT = QPoint(2, 22 * lenh + lenh / 2);
    byte8UINT = QPoint(2, 23 * lenh + lenh / 2);
    byte8OCT = QPoint(2, 24 * lenh + lenh / 2);

    byte1BASCII = QPoint(3, 1 * lenh + lenh / 2);
    byte1BBIN = QPoint(3, 2 * lenh + lenh / 2);
    byte1BHEX = QPoint(3, 3 * lenh + lenh / 2);
    byte1BINT = QPoint(3, 4 * lenh + lenh / 2);
    byte1BUINT = QPoint(3, 5 * lenh + lenh / 2);
    byte1BOCT = QPoint(3, 6 * lenh + lenh / 2);

    byte2BBIN = QPoint(3, 8 * lenh + lenh / 2);
    byte2BHEX = QPoint(3, 9 * lenh + lenh / 2);
    byte2BINT = QPoint(3, 10 * lenh + lenh / 2);
    byte2BUINT = QPoint(3, 11 * lenh + lenh / 2);
    byte2BOCT = QPoint(3, 12 * lenh + lenh / 2);

    byte4BBIN = QPoint(3, 14 * lenh + lenh / 2);
    byte4BHEX = QPoint(3, 15 * lenh + lenh / 2);
    byte4BINT = QPoint(3, 16 * lenh + lenh / 2);
    byte4BUINT = QPoint(3, 17 * lenh + lenh / 2);
    byte4BOCT = QPoint(3, 18 * lenh + lenh / 2);

    byte8BBIN = QPoint(3, 20 * lenh + lenh / 2);
    byte8BHEX = QPoint(3, 21 * lenh + lenh / 2);
    byte8BINT = QPoint(3, 22 * lenh + lenh / 2);
    byte8BUINT = QPoint(3, 23 * lenh + lenh / 2);
    byte8BOCT = QPoint(3, 24 * lenh + lenh / 2);

    model = new TableModel();
    ui->tableView->setModel(model);

    model->setData(data);

    ui->tableView->horizontalHeader()->setDefaultSectionSize(24);
    ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
    ui->tableView->setColumnWidth(0, 70);

    QWidget *oldTitle = ui->dockWidget1->titleBarWidget();
    ui->dockWidget1->setTitleBarWidget(new QWidget(this));
    delete oldTitle;

    oldTitle = ui->dockWidget->titleBarWidget();
    ui->dockWidget->setTitleBarWidget(new QWidget(this));
    delete oldTitle;

    connect(ui->tableView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(onSelectChange(QModelIndex)));
    connect(ui->tableWidgetBookmark, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(bookmarkDoubleClick(int,int)));
    connect(model, SIGNAL(searchResult(int)), this, SLOT(onSearchResult(int)));
}

BinView::~BinView()
{
    delete ui;
    delete model;
}


void BinView::onSelectChange(QModelIndex index)
{
    int colum1 = 2;
    int columb = 3;

    if (!index.isValid())
    {
        return;
    }

    currentIndex = index;

    QByteArray byte1 = this->model->getBytes(index, 1);
    QByteArray byte2 = this->model->getBytes(index, 2);
    QByteArray byte4 = this->model->getBytes(index, 4);
    QByteArray byte8 = this->model->getBytes(index, 8);

    if (byte1.isEmpty())
    {
        return;
    }

    if (byte1.at(0) > 0)
    {
        ui->treeWidget->itemAt(byte1ASCII)->setText(colum1, QString(byte1.at(0)));
        ui->treeWidget->itemAt(byte1BASCII)->setText(columb, QString(byte1.at(0)));
    }
    else
    {
        ui->treeWidget->itemAt(byte1ASCII)->setText(colum1, "");
        ui->treeWidget->itemAt(byte1BASCII)->setText(columb, "");
    }
    ui->treeWidget->itemAt(byte1BIN)->setText(2, QString::number((unsigned char)byte1.at(0), 2));
    ui->treeWidget->itemAt(byte1HEX)->setText(2, QString::number((unsigned char)byte1.at(0), 16));
    ui->treeWidget->itemAt(byte1INT)->setText(2, QString::number(byte1.at(0), 10));
    ui->treeWidget->itemAt(byte1UINT)->setText(2, QString::number((unsigned char)byte1.at(0), 10));
    ui->treeWidget->itemAt(byte1OCT)->setText(2, QString::number((unsigned char)byte1.at(0), 8));

    ui->treeWidget->itemAt(byte1BBIN)->setText(3, QString::number((unsigned char)byte1.at(0), 2));
    ui->treeWidget->itemAt(byte1BHEX)->setText(3, QString::number((unsigned char)byte1.at(0), 16));
    ui->treeWidget->itemAt(byte1BINT)->setText(3, QString::number(byte1.at(0), 10));
    ui->treeWidget->itemAt(byte1BUINT)->setText(3, QString::number((unsigned char)byte1.at(0), 10));
    ui->treeWidget->itemAt(byte1BOCT)->setText(3, QString::number((unsigned char)byte1.at(0), 8));

    if (byte2.length() != 2)
    {
        return;
    }

    unsigned short sv = (((unsigned short)byte2.at(0) << 8) & 0xFF00) | ((unsigned short)byte2.at(1) & 0xFF);
    ui->treeWidget->itemAt(byte2BIN)->setText(2, QString::number(sv, 2));
    ui->treeWidget->itemAt(byte2HEX)->setText(2, QString::number(sv, 16));
    ui->treeWidget->itemAt(byte2UINT)->setText(2, QString::number(sv, 10));
    ui->treeWidget->itemAt(byte2OCT)->setText(2, QString::number(sv, 8));
    ui->treeWidget->itemAt(byte2INT)->setText(2, QString::number((short)sv, 10));


    sv = (((unsigned short)byte2.at(1) << 8) & 0xFF00) | ((unsigned short)byte2.at(0) & 0xFF);
    ui->treeWidget->itemAt(byte2BBIN)->setText(3, QString::number(sv, 2));
    ui->treeWidget->itemAt(byte2BHEX)->setText(3, QString::number(sv, 16));
    ui->treeWidget->itemAt(byte2BUINT)->setText(3, QString::number(sv, 10));
    ui->treeWidget->itemAt(byte2BOCT)->setText(3, QString::number(sv, 8));
    ui->treeWidget->itemAt(byte2BINT)->setText(3, QString::number((short)sv, 10));

    if (byte4.length() != 4)
    {
        return;
    }
    unsigned int iv = (((unsigned int)byte4.at(0) << 24) & 0xFF000000) | (((unsigned int)byte4.at(1) << 16) & 0xFF0000) | (((unsigned int)byte4.at(2) << 8) & 0xFF00) | ((unsigned int)byte4.at(3) & 0xFF);
    ui->treeWidget->itemAt(byte4BIN)->setText(2, QString::number(iv, 2));
    ui->treeWidget->itemAt(byte4HEX)->setText(2, QString::number(iv, 16));
    ui->treeWidget->itemAt(byte4UINT)->setText(2, QString::number(iv, 10));
    ui->treeWidget->itemAt(byte4OCT)->setText(2, QString::number(iv, 8));
    ui->treeWidget->itemAt(byte4INT)->setText(2, QString::number((int)iv, 10));

    iv = (((unsigned int)byte4.at(3) << 24) & 0xFF000000) | (((unsigned int)byte4.at(2) << 16) & 0xFF0000) | (((unsigned int)byte4.at(1) << 8) & 0xFF00) | ((unsigned int)byte4.at(0) & 0xFF);
    ui->treeWidget->itemAt(byte4BBIN)->setText(3, QString::number(iv, 2));
    ui->treeWidget->itemAt(byte4BHEX)->setText(3, QString::number(iv, 16));
    ui->treeWidget->itemAt(byte4BUINT)->setText(3, QString::number(iv, 10));
    ui->treeWidget->itemAt(byte4BOCT)->setText(3, QString::number(iv, 8));
    ui->treeWidget->itemAt(byte4BINT)->setText(3, QString::number((int)iv, 10));

    if (byte8.length() != 8)
    {
        return;
    }
    unsigned long long lv = (((unsigned long long)byte8.at(0) << 56) & 0xFF00000000000000) | (((unsigned long long)byte8.at(1) << 48) & 0xFF000000000000) | (((unsigned long long)byte8.at(2) << 40) & 0xFF0000000000) | (((unsigned long long)byte8.at(3) << 32) & 0xFF00000000)
            | (((unsigned long long)byte8.at(4) << 24) & 0xFF000000) | (((unsigned long long)byte8.at(5) << 16) & 0xFF0000) | (((unsigned long long)byte8.at(6) << 8) & 0xFF00) | ((unsigned long long)byte8.at(7) & 0xFF);
    ui->treeWidget->itemAt(byte8BIN)->setText(2, QString::number(lv, 2));
    ui->treeWidget->itemAt(byte8HEX)->setText(2, QString::number(lv, 16));
    ui->treeWidget->itemAt(byte8UINT)->setText(2, QString::number(lv, 10));
    ui->treeWidget->itemAt(byte8OCT)->setText(2, QString::number(lv, 8));
    ui->treeWidget->itemAt(byte8INT)->setText(2, QString::number((long long)lv, 10));

    lv = (((unsigned long long)byte8.at(7) << 56) & 0xFF00000000000000) | (((unsigned long long)byte8.at(6) << 48) & 0xFF000000000000) | (((unsigned long long)byte8.at(5) << 40) & 0xFF0000000000) | (((unsigned long long)byte8.at(4) << 32) & 0xFF00000000)
                | (((unsigned long long)byte8.at(3) << 24) & 0xFF000000) | (((unsigned long long)byte8.at(2) << 16) & 0xFF0000) | (((unsigned long long)byte8.at(1) << 8) & 0xFF00) | ((unsigned long long)byte8.at(0) & 0xFF);
    ui->treeWidget->itemAt(byte8BIN)->setText(3, QString::number(lv, 2));
    ui->treeWidget->itemAt(byte8HEX)->setText(3, QString::number(lv, 16));
    ui->treeWidget->itemAt(byte8UINT)->setText(3, QString::number(lv, 10));
    ui->treeWidget->itemAt(byte8OCT)->setText(3, QString::number(lv, 8));
    ui->treeWidget->itemAt(byte8INT)->setText(3, QString::number((long long)lv, 10));
}

void BinView::setAddrFormat(DISPLAY_FORMAT_EN format)
{
    model->setAddrFormat(format);
}

void BinView::setFirstFormat(DISPLAY_FORMAT_EN format)
{
    int w = 24;
    model->setFirstFormat(format);

    if (format == FORMAT_BIN)
    {
        w = 68;
    }
    else if (format == FORMAT_CHAR || format == FORMAT_HEX)
    {
        w = 24;
    }
    else
    {
        w = 32;
    }

    ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
    for (int i=1; i<17; i++) {
        ui->tableView->setColumnWidth(i, w);
    }
    ui->tableView->setColumnWidth(0, 70);
}

void BinView::setSecFormat(DISPLAY_FORMAT_EN format)
{
    int w = 24;
    model->setSecFormat(format);

    if (format == FORMAT_BIN)
    {
        w = 68;
    }
    else if (format == FORMAT_CHAR || format == FORMAT_HEX)
    {
        w = 24;
    }
    else
    {
        w = 32;
    }

    ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
    for (int i=17; i<33; i++) {
        ui->tableView->setColumnWidth(i, w);
    }
    ui->tableView->setColumnWidth(0, 70);
}

void BinView::setFirstEndian(DISPLAY_ENDIAN_EN format)
{
    model->setFirstEndian(format);
}

void BinView::setSecEndian(DISPLAY_ENDIAN_EN format)
{
    model->setSecEndian(format);
}


void BinView::showBookmark()
{
    ui->tabWidget->setCurrentIndex(0);
}

void BinView::showSearchResult()
{
    ui->tabWidget->setCurrentIndex(1);
}

void BinView::addBookmark(QString title, QString remark)
{
    int addr = 0;
    int col = 0;

    if (currentIndex.column() < 17)
    {
        col -= 1;
    }

    if (currentIndex.column() >= 17)
    {
        col -= 17;
    }

    if (col < 0 || col > 0xF)
    {
        col = 0;
    }

    addr = currentIndex.row() * 16 + col;

    ui->tabWidget->setCurrentIndex(0);

    QByteArray bytes = model->getBytes(currentIndex, 16);

    QString hex;

    for (int i=0; i<bytes.length(); i++)
    {
        unsigned char c = (unsigned char)bytes.at(i);
        hex.append(QString("%1 ").arg(c, 2, 16, QLatin1Char('0')).toUpper());
    }

    int row = ui->tableWidgetBookmark->rowCount();
    ui->tableWidgetBookmark->setRowCount(row + 1);
    bookmarkList.append(currentIndex);

    ui->tableWidgetBookmark->setItem(row, 0, new QTableWidgetItem(title));
    ui->tableWidgetBookmark->setItem(row, 1, new QTableWidgetItem(QString("%1 ").arg(addr, 8, 16, QLatin1Char('0')).toUpper()));
    ui->tableWidgetBookmark->setItem(row, 2, new QTableWidgetItem(remark));
    ui->tableWidgetBookmark->setItem(row, 3, new QTableWidgetItem(hex));
}

bool BinView::hasValidIndex()
{
    return currentIndex.isValid();
}


void BinView::bookmarkDoubleClick(int x, int y)
{
    if (x < ui->tableWidgetBookmark->rowCount() && x < bookmarkList.count())
    {
        ui->tableView->scrollTo(bookmarkList.at(x));
    }
}

void BinView::searchResultClick(int x, int y)
{
    QTableWidget *tw = (QTableWidget *)ui->toolBoxSearch->currentWidget();

    bool ok = false;
    int addr = tw->item(x, 0)->text().toInt(&ok, 16);

    int row = 0, col = 0;
    row = addr / 16;
    col = addr % 16;

    ui->tableView->scrollTo(model->index(row, col));
}


void BinView::search(QByteArray bytes, int searchType, bool current)
{
    int addr = 0;
    int col = 0;
    QString searchContent = "";
    QString title = "";

    if (current)
    {
        if (!currentIndex.isValid())
        {
            addr = 0;
        }
        else
        {
            if (currentIndex.column() < 17)
            {
                col -= 1;
            }

            if (currentIndex.column() >= 17)
            {
                col -= 17;
            }

            if (col < 0 || col > 0xF)
            {
                col = 0;
            }

            addr = currentIndex.row() * 16 + col;
        }
    }

    if (searchType == 0)
    {
        searchContent = QString(bytes);
    }
    else if (searchType == 1)
    {
        for (int i=0; i<bytes.length(); i++)
        {
            unsigned char c = (unsigned char)bytes.at(i);
            searchContent.append(QString("%1 ").arg(c, 2, 16, QLatin1Char('0')).toUpper());
        }
    }


    title = tr("Search [") + searchContent + "] " + tr("From ") + "0x" + QString::number(addr, 16);

    QTableWidget *tw = new QTableWidget();
    searchContentList.append(bytes);
    searchTypeList.append(current);
    searchAddrList.append(addr);
    searchRViewList.append(tw);
    ui->toolBoxSearch->addItem(tw, QIcon(), title);
    ui->toolBoxSearch->setCurrentIndex(ui->toolBoxSearch->count() - 1);
    ui->tabWidget->setCurrentIndex(1);


    tw->setColumnCount(2);
    QList<QString> header;
    header << "Addr." << "Content";
    tw->setHorizontalHeaderLabels(header);

    ui->toolBoxSearch->show();

    connect(tw, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(searchResultClick(int,int)));

    model->search(bytes, addr);
}


void BinView::onSearchResult(int index)
{
    QTableWidget *tw = searchRViewList.last();
    if (!tw)
    {
        return;
    }

    int row = 0, col = 0;
    row = index / 16;
    col = index % 16;

    QModelIndex m = model->index(row, col + 1);

    QByteArray bytes = model->getBytes(m, 16);

    QString searchContent;

    for (int i=0; i<bytes.length(); i++)
    {
        unsigned char c = (unsigned char)bytes.at(i);
        searchContent.append(QString("%1 ").arg(c, 2, 16, QLatin1Char('0')).toUpper());
    }


    int rowCount = tw->rowCount();
    tw->setRowCount(rowCount + 1);

    //qDebug() << rowCount << QString::number(index, 16) << searchContent;


    tw->setItem(rowCount, 0, new QTableWidgetItem("0x" + QString::number(index, 16)));
    tw->setItem(rowCount, 1, new QTableWidgetItem(searchContent));
}

void BinView::onSearchEnd(int count, int start)
{

}


void BinView::gotoLine(int index)
{
    int row = 0, col = 0;
    row = index / 16;
    col = index % 16;

    ui->tableView->scrollTo(model->index(row, col));
}
